﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using Sanford.Multimedia.Midi;

namespace Brio
{
    public partial class OutputScreen : Form
    {
        public OutputScreen()
        {
            InitializeComponent();
            SavedChromosomeItems = new List<ChromosomeListItem>();
            OutputChromosomeItems = new List<ChromosomeListItem>();

            DeleteTemporaryFolder();
            CreateRequiredFolders();
            
            brio = new Brio();

            brio.GenerationChanged += new GenerationChangedEventHandler(brio_GenerationChanged);

            KeyComboBox.Items.Add(Sanford.Multimedia.Key.CMajor);
            KeyComboBox.Items.Add(Sanford.Multimedia.Key.EFlatMajor);
            KeyComboBox.SelectedIndex = 0;
            KeyComboBox.Enabled = false;

            TechniqueCollectionComboBox.Items.AddRange(Framework.TechniqueCollection.SampleCollectionNames);

            TechniqueCollectionComboBox.SelectedIndex = 1;

            IntervalComboBox.Items.AddRange(Framework.IntervalRelationshipTable.IntervalRelationshipTableNames);

            IntervalComboBox.SelectedIndex = 1;


            /*
            foreach (Sanford.Multimedia.Key key in Enum.GetValues(typeof(Sanford.Multimedia.Key)))
            {
                KeyComboBox.Items.Add(key.ToString());
            }

            Array keys = Enum.GetValues(typeof(Sanford.Multimedia.Key));
            */

        }


        public Brio brio;

        public List<ChromosomeListItem> SavedChromosomeItems;

        public List<Framework.Technique> BoundTechniqueList;
        public List<IntervalDataItem> BoundIntervalRelationshipTable;
        public double BoundedIntervalRelationshipMultiplier = 0.0;

        public List<MotifChromosome> OutputChromosomes;
        public List<ChromosomeListItem> OutputChromosomeItems;


        private OutputDevice outDevice;

        private int outDeviceID = 0;

        public void DeleteTemporaryFolder()
        {
            // Create Temporary Folder
            if (Directory.Exists(Directory.GetCurrentDirectory() + "/Temp"))
            {
                Directory.Delete(Directory.GetCurrentDirectory() + "/Temp", true);
            }
        }


        public void CreateRequiredFolders()
        {
            string currentDirectory = Directory.GetCurrentDirectory();
            
            // Create Output Folder
            if (Directory.Exists(Directory.GetCurrentDirectory() + "/Output"))
            {
                Directory.CreateDirectory(Directory.GetCurrentDirectory() + "/Output");
            }
            // Create Temporary Folder
            if (Directory.Exists(Directory.GetCurrentDirectory() + "/Temp"))
            {
                Directory.CreateDirectory(Directory.GetCurrentDirectory() + "/Temp");
            }

        }


        private void OutputScreen_FormClosing(object sender, FormClosingEventArgs e)
        {
            // Release all player / viewer / device resources
            OutputPlayerControl.ResetPlayer();
            OutputSheetMusicViewer.ResetViewer();
            outDevice.Reset();
            playerControl1.ResetPlayer();
            sheetMusicViewerControl1.ResetViewer();

        }


        private void OutputScreen_Load(object sender, EventArgs e)
        {
            if (OutputDevice.DeviceCount == 0)
            {
                //MessageBox.Show("No MIDI output devices available. Player is disabled.", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);

                // Unusable!
                Enabled = false;
            }
            else
            {
                try
                {
                    outDevice = new OutputDevice(outDeviceID);

                }
                catch (Exception ex)
                {
                    // MessageBox.Show(ex.Message + " Player is disabled.", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);

                    // Unusable!
                    Enabled = false;
                }
            }

        }



        /*
        private void GenerateButton_Click(object sender, EventArgs e)
        {
            string saveDirectory = Directory.GetCurrentDirectory() + "/Output/" + GenerateTextBox.Text;

            testChromosome.Staff.Tempo = (int)TempoUpDown.Value;

            brio.SaveChromosome(outDevice, testChromosome, saveDirectory, this.chromosomeNumber);

            Text = "Brio - Last generated chromosome was : " + GenerateTextBox.Text;


            SetUI(false);
            ResetUI();

        }
        */

        private void ProgramTabControl_SelectedIndexChanged(object sender, EventArgs e)
        {
            // We're loading the analyze tab, refresh the listview and load the first index
            if (ProgramTabControl.SelectedTab.Text == "Saved Chromosomes")
            {
                // Clear Items
                MidiListbox.Items.Clear();
                SavedChromosomeItems.Clear();

                // Get Chromosome Directories
                string[] chromosomeDirectories = Directory.GetDirectories(System.IO.Directory.GetCurrentDirectory() +
                                                                                "/Output");

                foreach (string chromosomeDirectory in chromosomeDirectories)
                {
                    if (!chromosomeDirectory.Contains(".svn"))
                    {
                        SavedChromosomeItems.Add(new ChromosomeListItem(chromosomeDirectory));

                        // I have a lot more to expand on the viewer controls and eventually i want to read in the name
                        // and info about the chromosome from and XML and set the name based on that but for now
                        // I'd like a pretty looking name so just get the directory name (not the whole path)
                        string prettyName = chromosomeDirectory.Substring(chromosomeDirectory.LastIndexOf("\\") + 1);

                        MidiListbox.Items.Add(prettyName);
                    }
                }



                if (MidiListbox.Items.Count > 0)
                {
                    MidiListbox.SelectedIndex = 0;
                }
            }
            else
            {
                playerControl1.ResetPlayer();
            }
        }



        public void LoadChromosomeItems(int index, List<ChromosomeListItem> chromosomeItems,
                                        PlayerControl playerControl,
                                        SheetMusicViewerControl viewerControl,
                                        RichTextBox metricControl)
        {
           // Load the MIDI
            if (!string.IsNullOrWhiteSpace(chromosomeItems[index].MIDIPath))
           {
               playerControl.ResetPlayer();
               playerControl.LoadFile(chromosomeItems[index].MIDIPath, outDevice);
           }
           // Load the Sheet Music Viewer
            if (!string.IsNullOrWhiteSpace(chromosomeItems[index].SheetMusicDirectoryPath))
           {
               viewerControl.LoadDirectoryImages(chromosomeItems[index].SheetMusicDirectoryPath, "png");
           }

            if (metricControl != null &&
                !string.IsNullOrEmpty(chromosomeItems[index].MetricPath))
            {
                StreamReader reader = new StreamReader(chromosomeItems[index].MetricPath);

                string report = reader.ReadToEnd();

                reader.Close();

                metricControl.Text = report;
            }
            else
            {
                metricControl.Text = "";
            }


        }

        private void MidiListbox_SelectedIndexChanged(object sender, EventArgs e)
        {
            LoadChromosomeItems(MidiListbox.SelectedIndex, SavedChromosomeItems, playerControl1, sheetMusicViewerControl1, SavedMetricListBox);
        }

        public void SetNewTechniqueCollection()
        {
            Framework.TechniqueCollection collection = new Framework.TechniqueCollection();

            foreach (Framework.Technique technique in BoundTechniqueList)
            {
                collection.Add(technique.Name, technique);
            }

            brio.Techniques = collection;
        }

        public void SetNewIntervalRelationship()
        {
            Framework.IntervalRelationshipTable relationshipTable = new Framework.IntervalRelationshipTable();

            foreach (IntervalDataItem dataItem in BoundIntervalRelationshipTable)
            {
                relationshipTable.Add(dataItem.Interval, dataItem.MelodicValue);
            }

            brio.IntervalRelationships = relationshipTable;
            brio.IntervalRelationships.RelationshipMultiplier = BoundedIntervalRelationshipMultiplier;
        }

        public void SetMusicalOperators()
        {
            brio.MusicalOperators = new Framework.MusicalOperatorCollection();
            brio.MusicalOperators.AddOperators();
        }

        private void RunButton_Click(object sender, EventArgs e)
        {
            SetBrioParametersAndSettings();

            SetNewTechniqueCollection();

            SetNewIntervalRelationship();

            SetMusicalOperators();

            // Set Progress Bar for Sheet Music and MIDI saving
            ProgressBar.Maximum = Framework.Settings.Generations;
            ProgressBar.Value = 0;
            ProgressLabel.Text = "Generation " + ProgressBar.Value + " of " + ProgressBar.Maximum;
            ProgressLabel.Invalidate();

            // Will report progress of runs using the GenerationChanged event.
            OutputChromosomes = brio.Run();


            // Set Progress Bar for Sheet Music and MIDI saving
            ProgressBar.Maximum = OutputChromosomes.Count;
            ProgressBar.Value = 0;
            ProgressLabel.Text = "Saved " + ProgressBar.Value + " of " + ProgressBar.Maximum + " Chromosomes";

            for (int i = 0; i < OutputChromosomes.Count; i++)
            {
                string prettyNumber = (i == 0) ? "000" : ((i < 10) ? "00" + i.ToString() : ((i < 100) ? "0" + i.ToString() : i.ToString()));

                string chromosomeDirectory = Directory.GetCurrentDirectory() + "/Temp/" + prettyNumber;

                if (Directory.Exists(chromosomeDirectory))
                {
                    Directory.Delete(chromosomeDirectory, true);
                }

                brio.SaveChromosome(outDevice, OutputChromosomes[i], chromosomeDirectory);

                ProgressBar.Value++;
                ProgressLabel.Text = "Saved " + ProgressBar.Value + " of " + ProgressBar.Maximum + " Chromosomes";
                ProgressLabel.Invalidate();
                ProgressBar.Invalidate();
            }

            // Reset
            ProgressLabel.Text = "Please review your run.";

        }


        public void SetBrioParametersAndSettings()
        {
            // Music Customization Parameters
            Framework.Settings.Measures = (int)MeasuresUpDown.Value;
            Framework.Settings.TimeSignature = new int[] { 4, 4 };
            Framework.Settings.Tempo = (int)TempoUpDown.Value;

            // Genetic Algorithm Parameters
            Framework.Settings.PopulationSize = (int)PopulationUpDown.Value;
            Framework.Settings.Generations = (int)GenerationsUpDown.Value;
            Framework.Settings.BestPercentage = (int)BestPercentageUpDown.Value;
            Framework.Settings.ExploratoryRatio = (double)ExploratoryUpDown.Value;

            // Musical Operator Parameters
            Framework.Settings.GaussianMutationPercentage = (int)GaussianMutationUpDown.Value;
            Framework.Settings.GaussianMutationSigma = (int)GaussianSigmaUpDown.Value;
            Framework.Settings.CrossoverOperatorPercentage = (int)CrossoverUpDown.Value;
            Framework.Settings.InjectOperatorPercentage = (int)InjectUpDown.Value;
            Framework.Settings.MelodicSmoothingPercentage = (int)SmoothingUpDown.Value;
            Framework.Settings.JoinOperatorPercentage = (int)JoinUpDown.Value;
            Framework.Settings.SplitOperatorPercentage = (int)SplitUpDown.Value;


        }

        private void RunTabControl_SelectedIndexChanged(object sender, EventArgs e)
        {
            // We're loading the analyze tab, refresh the listview and load the first index
            if (RunTabControl.SelectedTab.Name.Equals("ReviewPage", StringComparison.InvariantCultureIgnoreCase) && 
                Directory.Exists(System.IO.Directory.GetCurrentDirectory() + "/Temp"))
            {
                // Clear Previous items
                OutputMIDIListbox.Items.Clear();
                OutputChromosomeItems.Clear();

                // Get Temporary Directory
                string[] chromosomeDirectories = Directory.GetDirectories(System.IO.Directory.GetCurrentDirectory() +
                                                                                "/Temp");

                foreach (string chromosomeDirectory in chromosomeDirectories)
                {
                    if (!chromosomeDirectory.Contains(".svn"))
                    {
                        OutputChromosomeItems.Add(new ChromosomeListItem(chromosomeDirectory));

                        // I have a lot more to expand on the viewer controls and eventually i want to read in the name
                        // and info about the chromosome from and XML and set the name based on that but for now
                        // I'd like a pretty looking name so just get the directory name (not the whole path)
                        string prettyName = chromosomeDirectory.Substring(chromosomeDirectory.LastIndexOf("\\") + 1);

                        OutputMIDIListbox.Items.Add(prettyName);
                    }
                }

                if (OutputMIDIListbox.Items.Count > 0)
                {
                    OutputMIDIListbox.SelectedIndex = 0;
                }
            }
            else
            {
                OutputPlayerControl.ResetPlayer();
                OutputSheetMusicViewer.ResetViewer();
            }
        }

        private void OutputMIDIListbox_SelectedIndexChanged(object sender, EventArgs e)
        {
            LoadChromosomeItems(OutputMIDIListbox.SelectedIndex, OutputChromosomeItems, OutputPlayerControl, OutputSheetMusicViewer, OutputMetricsTextbox);
        }


        public void brio_GenerationChanged(object sender, int generation)
        {
            ProgressBar.Value = generation + 1; // because Brio Generations are 0 indexed
            ProgressLabel.Text = "Generation " + ProgressBar.Value + " of " + ProgressBar.Maximum;

            ProgressLabel.Invalidate();
            ProgressBar.Invalidate();
            Application.DoEvents(); // GRR, UPDATE MY PROGRESS LABEL I SAID!
        }


        private void SaveSelectedButton_Click(object sender, EventArgs e)
        {
            // Release resources of player and viewer
            OutputPlayerControl.ResetPlayer();
            OutputSheetMusicViewer.ResetViewer();

            string saveName = "SavedChromos_" + Framework.Constants.Random.Next(10000);

            for (int i = 0; i < OutputMIDIListbox.CheckedIndices.Count; i++)
            {
                int index = OutputMIDIListbox.CheckedIndices[i];

               string prettyName = OutputChromosomeItems[index].DirectoryPath.Substring(OutputChromosomeItems[index].DirectoryPath.LastIndexOf("\\") + 1);


               Framework.HelperMethods.CopyDirectory(OutputChromosomeItems[index].DirectoryPath, 
                                                    Directory.GetCurrentDirectory() + "/Output/" + saveName + "_" + prettyName); 
            }

            Text = "Chromosomes saved under the following name : " + saveName;
        }

        private void TechniqueCollectionComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Make a technique collection
            Framework.TechniqueCollection techniques = new Framework.TechniqueCollection();

            techniques.LoadCollection((string)TechniqueCollectionComboBox.SelectedItem);

            // strip away the dictionary<key, pair> nonsense and allow it to bind properly.
            BoundTechniqueList = new List<Framework.Technique>(techniques.Values);


            TechniqueDataGridView.DataSource = BoundTechniqueList;
        }

        private void IntervalComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            Framework.IntervalRelationshipTable table = new Framework.IntervalRelationshipTable();

            table.LoadRelationshipTable((string)IntervalComboBox.SelectedItem);

            BoundedIntervalRelationshipMultiplier = table.RelationshipMultiplier;

            BoundIntervalRelationshipTable = new List<IntervalDataItem>();

            for (int i = 0; i < table.Count; i++)
            {
                IntervalDataItem item = new IntervalDataItem(table.ElementAt(i).Key, table.ElementAt(i).Value);

                BoundIntervalRelationshipTable.Add(item);
            }

            IntervalRelationshipDataGridView.DataSource = BoundIntervalRelationshipTable;
        }


    }


    public class IntervalDataItem
    {
        public IntervalDataItem(int key, double value)
        {
            this.key = key;
            this.value = value;
        }

        private int key;
        private double value;

        public int Interval { get { return key; } }
        public double MelodicValue { get { return value; } set { this.value = value; } }
    }
}
